using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;

namespace Utility.GameEvents.Scriptable.Editor {
    public class GameEventDebugger : EditorWindow
    {
        private Vector2 m_scroll;

        [MenuItem("Tools/GameEvent Debugger")]
        public static void ShowWindow()
        {
            GetWindow<GameEventDebugger>("GameEvent Debugger");
        }

        private void OnGUI()
        {
            EditorGUILayout.LabelField("🎯 GameEvent 调试面板", EditorStyles.boldLabel);
            Color oldColor = GUI.contentColor;
            GUI.contentColor = Color.red;
            if(Application.isEditor) EditorGUILayout.LabelField("编辑器模式下不支持完整功能！", EditorStyles.boldLabel);
            GUI.contentColor = oldColor; // 恢复原颜色
            
            EditorGUILayout.Space();

            var events = GetAllGameEvents();

            m_scroll = EditorGUILayout.BeginScrollView(m_scroll);

            foreach (var e in events)
            {
                DrawEventBlock(e);
            }

            EditorGUILayout.EndScrollView();
        }

        private void DrawEventBlock(ScriptableObject obj)
        {
            var type = obj.GetType();

            EditorGUILayout.BeginVertical("box");
            EditorGUILayout.LabelField($"📦 {obj.name} ({type.Name})", EditorStyles.boldLabel);

            DrawStats(obj, type);
            DrawListeners(obj, type);

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space(5);
        }

        private void DrawStats(ScriptableObject obj, System.Type type)
        {
            var raiseCount = type.GetField("raiseCount")?.GetValue(obj);
            var totalTime = type.GetField("totalRaiseTimeMs")?.GetValue(obj);
            var lastValue = type.GetField("lastRaisedValue")?.GetValue(obj);

            EditorGUILayout.LabelField($"▶️ Raised: {raiseCount ?? 0} 次");
            EditorGUILayout.LabelField($"⏱️ 总耗时: {totalTime:F2} ms");
            EditorGUILayout.LabelField($"📥 最后值: {lastValue?.ToString() ?? "null"}");
            EditorGUILayout.Space();
        }
        
        private static FieldInfo FindFieldInHierarchy(System.Type type, string fieldName)
        {
            while (type != null)
            {
                var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
                if (field != null) return field;
                type = type.BaseType;
            }
            return null;
        }

        private void DrawListeners(ScriptableObject obj, System.Type type)
        {
            var listenerField = FindFieldInHierarchy(obj.GetType(), "_listeners");
            var listeners = listenerField?.GetValue(obj) as IEnumerable<object>;
            
            if (listeners == null) return;

            foreach (var l in listeners)
            {
                var mb = l as MonoBehaviour;
                if (mb == null)
                {
                    EditorGUILayout.LabelField("❌ Unknown Listener", EditorStyles.helpBox);
                    continue;
                }

                // EditorGUILayout.LabelField($"👂 {mb.name} ({l.GetType().Name})", EditorStyles.miniBoldLabel);
                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button($"👂 {mb.name} ({l.GetType().Name})", EditorStyles.linkLabel))
                {
                    Debug.Log($"👂 {mb.name} ({l.GetType().Name})");
                    EditorGUIUtility.PingObject(mb.gameObject);
                    Selection.activeObject = mb.gameObject;
                }
                EditorGUILayout.EndHorizontal();

                DrawBindingValidation(l);
            }
        }

        private void DrawBindingValidation(object listenerObj)
        {
            var type = listenerObj.GetType();
            var responseField = type.GetField("response", BindingFlags.NonPublic | BindingFlags.Instance);
            var unityEvent = responseField?.GetValue(listenerObj) as UnityEventBase;

            if (unityEvent == null)
            {
                EditorGUILayout.HelpBox("⚠️ 未找到 UnityEvent 绑定字段。", MessageType.Warning);
                return;
            }

            for (int i = 0; i < unityEvent.GetPersistentEventCount(); i++)
            {
                var target = unityEvent.GetPersistentTarget(i);
                var methodName = unityEvent.GetPersistentMethodName(i);

                if (target == null || string.IsNullOrEmpty(methodName))
                {
                    EditorGUILayout.HelpBox($"❌ [{i}] 未绑定对象或方法。", MessageType.Error);
                    continue;
                }

                var methodInfo = target.GetType().GetMethod(methodName,
                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                if (methodInfo == null)
                {
                    EditorGUILayout.HelpBox($"❌ [{i}] 找不到方法: {methodName}", MessageType.Error);
                    continue;
                }

                var parameters = methodInfo.GetParameters();

                if (parameters.Length == 0)
                {
                    EditorGUILayout.HelpBox($"❌ [{i}] {methodName}() 是静态绑定方法，缺少参数。", MessageType.Error);
                }
                else if (parameters.Length > 1)
                {
                    EditorGUILayout.HelpBox($"⚠️ [{i}] {methodName}() 有多个参数，仅支持一个。", MessageType.Warning);
                }
                else
                {
                    EditorGUILayout.HelpBox($"✅ [{i}] {methodName}({parameters[0].ParameterType.Name})", MessageType.Info);
                }
            }
        }

        private static List<ScriptableObject> GetAllGameEvents()
        {
            var result = new List<ScriptableObject>();
            string[] guids = AssetDatabase.FindAssets("t:ScriptableObject");

            foreach (var guid in guids)
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                var obj = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
                if (obj == null) continue;

                var type = obj.GetType();
                if (type.BaseType != null && type.BaseType.IsGenericType &&
                    type.BaseType.GetGenericTypeDefinition().Name.StartsWith("GameEvent"))
                {
                    result.Add(obj);
                }
            }

            return result;
        }
    }
}
